﻿/*
 * Created by SharpDevelop.
 * User: tdptcj
 * Date: 12/10/2011
 * Time: 17:12
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net;
using System.Windows.Controls;
using System.Windows.Media.Imaging;

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Threading;
using MetaVideoEditorEvolved.Helper;
using MetaVideoEditorEvolved.UI;
using mveEngine;

namespace MetaVideoEditorEvolved.Control
{
	/// <summary>
	/// Description of PosterControlVM.
	/// </summary>
	public class PosterControlVM : ViewModelBase
	{
		UI.Entity.PosterMediaItem poster;
		private string localPath;
		
		UI.Entity.VideoMediaElement video;
		
		public UI.Entity.VideoMediaElement Video {
			get
			{
				return this.video;
			}
			
			set
			{
				this.video = value;
				this.PropertyChangedOnUI("Video");
				
				this.SetPosterValues();
			}
		}
		
		bool isReinitializing;
		
		public bool IsReinitializing {
			get
			{
				return this.isReinitializing;
			}
			
			set
			{
				this.isReinitializing = value;
				this.PropertyChangedOnUI("IsReinitializing");
			}
		}
		
		double actualHeight;
		
		public double ActualHeight {
			get
			{
				return this.actualHeight;
			}
			
			set
			{
				this.actualHeight = value;
				this.PropertyChangedOnUI("ActualHeight");
			}
		}
		
		double actualWidth;
		
		public double ActualWidth {
			get
			{
				return this.actualWidth;
			}
			
			set
			{
				this.actualWidth = value;
				this.PropertyChangedOnUI("ActualWidth");
			}
		}
		
		bool isInError;
		
		public bool IsInError {
			get
			{
				return this.isInError;
			}
			
			set
			{
				this.isInError = value;
				this.PropertyChangedOnUI("IsInError");
			}
		}
		
		bool doThumbnail;
		
		public bool DoThumbnail {
			get
			{
				return this.doThumbnail;
			}
			
			set
			{
				this.doThumbnail = value;
				this.PropertyChangedOnUI("DoThumbnail");
			}
		}
		
		bool isPrimary;
		
		public bool IsPrimary {
			get
			{
				return this.isPrimary;
			}
			
			set
			{
				this.isPrimary = value;
				this.PropertyChangedOnUI("IsPrimary");
			}
		}
		
		string imageRatio;
		
		public string ImageRatio {
			get
			{
				return this.imageRatio;
			}
			
			set
			{
				this.imageRatio = value;
				this.PropertyChangedOnUI("ImageRatio");
			}
		}
		
		double imageHeight;
		
		public double ImageHeight {
			get
			{
				return this.imageHeight;
			}
			
			set
			{
				this.imageHeight = value;
				this.PropertyChangedOnUI("ImageHeight");
			}
		}
		
		double imageWidth;
		
		public double ImageWidth {
			get
			{
				return this.imageWidth;
			}
			
			set
			{
				this.imageWidth = value;
				this.PropertyChangedOnUI("ImageWidth");
			}
		}
		
		long totalBytesLoaded;
		
		public long TotalBytesLoaded {
			get
			{
				return this.totalBytesLoaded;
			}
			
			set
			{
				this.totalBytesLoaded = value;
				this.PropertyChangedOnUI("TotalBytesLoaded");
			}
		}
		
		long imageLength;
		
		public long ImageLength {
			get
			{
				return this.imageLength;
			}
			
			set
			{
				this.imageLength = value;
				this.PropertyChangedOnUI("ImageLength");
			}
		}
		
		bool hasAlreadyDownloadedRealImage = false;
		
		bool isImageLengthUnknow;
		
		public bool IsImageLengthUnknow {
			get
			{
				return this.isImageLengthUnknow;
			}
			
			set
			{
				this.isImageLengthUnknow = value;
				this.PropertyChangedOnUI("IsImageLengthUnknow");
			}
		}
		
		bool isLocalImage;
		
		public bool IsLocalImage {
			get
			{
				return this.isLocalImage;
			}
			
			set
			{
				this.isLocalImage = value;
				
				if (!this.IsLoading && this.Video!=null) {
					this.Video.HasChanged = true;
					this.Poster.Checked = value;
					
					var mveItem = KernelSessionManager.SessionInstance.RealKernel.ItemCollection.ItemsList.First(i=>i.Id == this.Video.Id.ToString());
					
					mveEngine.Poster poster = mveItem.ImagesPaths.FirstOrDefault(p=>p.Image == this.Poster.Image);
					if (poster!=null) {
						poster.Checked = this.IsLocalImage;
					}
					else
					{
						poster = mveItem.BackdropImagePaths.FirstOrDefault(p=>p.Image == this.Poster.Image);
						if (poster!=null) {
							poster.Checked = this.IsLocalImage;
						}
						else
						{
							poster = mveItem.BannersPaths.FirstOrDefault(p=>p.Image == this.Poster.Image);
							if (poster!=null) {
								poster.Checked = this.IsLocalImage;
							}
						}
					}
				}
				
				this.PropertyChangedOnUI("IsLocalImage");
			}
		}
		
		public UI.Entity.PosterMediaItem Poster {
			get
			{
				return this.poster;
			}
			
			set
			{
				this.poster = value;
				this.PropertyChangedOnUI("Poster");
				
				this.SetPosterValues();
			}
		}
		
		bool isLoading;
		
		public bool IsLoading {
			get
			{
				return this.isLoading;
			}
			
			set
			{
				this.isLoading = value;
				this.PropertyChangedOnUI("IsLoading");
			}
		}
		
		bool isPoster;
		
		public bool IsPoster {
			get
			{
				return this.isPoster;
			}
			
			set
			{
				this.isPoster = value;
				this.PropertyChangedOnUI("IsPoster");
			}
		}
		
		bool noImage;
		
		public bool NoImage {
			get
			{
				return this.noImage;
			}
			
			set
			{
				this.noImage = value;
				this.PropertyChangedOnUI("NoImage");
			}
		}
		
		string imagePath;
		
		public string ImagePath {
			get
			{
				return this.imagePath;
			}
			
			set
			{
				this.imagePath = value;
				this.PropertyChangedOnUI("ImagePath");
				
				if(this.poster==null && string.IsNullOrEmpty(this.localPath))
				{
					this.SetPosterValues();
				}
			}
		}
		
		Image imageObject;
		
		public Image ImageObject {
			get
			{
				return this.imageObject;
			}
			
			set
			{
				this.imageObject = value;
				this.PropertyChangedOnUI("ImageObject");
			}
		}
		
		Image zoomImageObject;
		
		public Image ZoomImageObject {
			get
			{
				return this.zoomImageObject;
			}
			
			set
			{
				this.zoomImageObject = value;
				this.PropertyChangedOnUI("ZoomImageObject");
			}
		}
		
		public PosterControlVM()
		{
			this.isInError = false;
			this.isPoster = false;
		}
		
		private void SetPosterValues()
		{
			if(!this.IsReinitializing && this.ZoomImageObject!=null)
			{
				this.NoImage = false;
				this.isPoster = false;
				this.isInError = false;
				
				this.ZoomImageObject.Source = null;
				this.ImageObject.Source = null;
				
				if(this.poster!=null && this.video!=null)
				{
					this.poster.PropertyChanged+= delegate(object sender, PropertyChangedEventArgs e) {
						if (sender == this.poster && e.PropertyName=="Checked") {
							this.isLocalImage = this.poster.Checked;
							this.PropertyChangedOnUI("IsLocalImage");
						}
					};
					
					this.IsPoster = true;
					this.IsPrimary = this.poster == this.Video.PrimaryBackdrop || this.poster == this.Video.PrimaryPoster || (this.Video is UI.Entity.SerieMediaItem && ((UI.Entity.SerieMediaItem)this.Video).PrimaryBanner == this.poster);
					
					Action handleImage = new Action(()=>
					                                {
					                                	this.HandleNewImage();
					                                });
					handleImage.BeginInvoke(null, null);
				}
				else
				{
					if (string.IsNullOrEmpty(this.ImagePath) && string.IsNullOrEmpty(this.ImagePath)) {
						this.NoImage = true;
						this.IsLoading = false;
						this.IsPrimary = true;
						this.IsPoster = false;
					}
					else
					{
						this.IsPoster = false;
						this.IsPrimary = true;
						
						Action handleImage = new Action(()=>
						                                {
						                                	this.HandleImageWithUri();
						                                });
						handleImage.BeginInvoke(null, null);
					}
				}
			}
		}
		
		private void HandleImageWithUri()
		{
			DispatcherHelper.CheckBeginInvokeOnUI(()=>{
			                                      	this.IsLoading = true;
			                                      	this.IsImageLengthUnknow = true;
			                                      });
			
			this.localPath = Path.Combine(ApplicationPaths.AppImagePath, mveEngine.Helper.GetMD5(this.ImagePath) + Path.GetExtension(this.ImagePath));
			if (!File.Exists(this.localPath)) {
				this.localPath = this.ImagePath;
			}
			
			if (File.Exists(this.localPath)) {
				this.DownloadLocalImageAsync(this.localPath, true);
			}
			else
			{
				if (this.ImagePath.IndexOf("http://")==-1) {
					this.DownloadLocalImageAsync(this.ImagePath, true);
				}
				else
				{
					this.DownloadFileAsync();
				}
			}
		}
		
		private void HandleNewImage()
		{
			try
			{
				DispatcherHelper.CheckBeginInvokeOnUI(()=>{
				                                      	this.IsLoading = true;
				                                      	this.IsImageLengthUnknow = true;
				                                      });
				// Detect if it is an available image
				this.localPath = Path.Combine(ApplicationPaths.AppImagePath, mveEngine.Helper.GetMD5(this.Poster.Image) + Path.GetExtension(this.Poster.Image));
				
				// Detect if id is present
				if (!string.IsNullOrEmpty(this.Poster.Image) && string.IsNullOrEmpty(this.Poster.Id)) {
					string id = string.Empty;
					
					// Try to read it from image file first
					if(File.Exists(localPath))
					{
						id =  BitmapMetadataHelper.ReadMetadata(localPath, TypeBitmapMetadata.UniqueId);
					}
					else
					{
						if (this.Poster.Image.IndexOf("http://") == -1) {
							id =  BitmapMetadataHelper.ReadMetadata(localPath, TypeBitmapMetadata.UniqueId);
						}
					}
					
					if (!string.IsNullOrEmpty(id)) {
						this.Poster.Id = id;
					}
				}

				// We are sure that the id is filled up
				// We will search if another image already exists with the same id
//				var posterToFind = this.Video.Posters.FirstOrDefault(p=>p.Id == this.Poster.Id && p!=this.Poster);
//
//				if (posterToFind!=null) {
//
//				}
//				else
//				{
//					lock(this.Video.Backdrops)
//					{
//						posterToFind = this.Video.Backdrops.FirstOrDefault(p=>p.Id == this.Poster.Id && p!=this.Poster);
//
//						if (posterToFind!=null) {
//							var posterToRemove = posterToFind == this.Video.PrimaryBackdrop ? this.Poster : posterToFind;
//
//							DispatcherHelper.CheckBeginInvokeOnUI(()=>{
//							                                      	try
//							                                      	{
//							                                      		var removed = this.Video.Backdrops.Remove(posterToRemove);
//							                                      		if (removed) {
//							                                      			Tracer.TracerInstance.TraceInformation("Poster removed from collection, duplicate detected");
//							                                      		}
//							                                      		else
//							                                      		{
//							                                      			Tracer.TracerInstance.TraceInformation("Poster was not removed from collection, duplicate detected");
//							                                      		}
//							                                      	}
//							                                      	catch(Exception ex)
//							                                      	{
//							                                      		Tracer.TracerInstance.TraceError("Error handling image in poster control", ex);
//							                                      		this.IsInError = true;
//							                                      	}
//							                                      });
//
//							if (posterToRemove == this.Poster) {
//								return;
//							}
//						}
//					}
//				}
				
				if (!string.IsNullOrEmpty(this.Poster.Image))
				{
					DispatcherHelper.CheckBeginInvokeOnUI(()=>{
					                                      	this.IsLocalImage = this.Poster.Checked;
					                                      });
				}
				
				// This is a file available on disk
				if (!string.IsNullOrEmpty(this.Poster.Image) && File.Exists(localPath) && localPath.IndexOf(ApplicationPaths.AppImagePath) == 0) {
					this.ImagePath = localPath;
					
					Action loadLocalFile = new Action(()=>
					                                  {
					                                  	this.DownloadLocalImageAsync(localPath, true);
					                                  });
					
					loadLocalFile.BeginInvoke(null, null);
				}
				else
				{
					// This file is either http or on another computer
					
					// This is a file on another computer and need to be copied on cache
					if (this.Poster.Image.IndexOf("http://") == -1) {
						File.Copy(this.Poster.Image, localPath);
						
						if(string.IsNullOrEmpty(this.Poster.Id))
						{
							this.Poster.Id = mveEngine.Helper.GetMD5(this.Poster.Image);
							
							BitmapMetadataHelper.WriteMetadata(localPath, TypeBitmapMetadata.UniqueId, mveEngine.Helper.GetMD5(this.Poster.Image));
						}
						
						// Maybe we don't have right to write on another computer
						try {
							if(string.IsNullOrEmpty(this.Poster.Id))
							{
								this.Poster.Id = mveEngine.Helper.GetMD5(this.Poster.Image);
								
								BitmapMetadataHelper.WriteMetadata(this.Poster.Image, TypeBitmapMetadata.UniqueId, this.Poster.Id);
							}
						} catch{
						}
						
						Action loadLocalFile = new Action(()=>
						                                  {
						                                  	this.DownloadLocalImageAsync(localPath, true);
						                                  });
						
						loadLocalFile.BeginInvoke(null, null);
					}
					else
					{
						Action downloadFile = new Action(()=>
						                                 {
						                                 	this.DownloadFileAsync();
						                                 });
						downloadFile.BeginInvoke(null, null);
					}
				}
			}
			catch(Exception ex)
			{
				Tracer.TracerInstance.TraceError("Error handling image in poster control", ex);
				this.IsInError = true;
			}
		}
		
		public void DownloadZoomImage()
		{
			lock(this)
			{
				if(!this.hasAlreadyDownloadedRealImage)
				{
					this.DownloadLocalImageAsync(this.localPath, false);
				}
			}
		}
		
		private void DownloadLocalImageAsync(string imagePath, bool isThumbnail)
		{
			Tracer.TracerInstance.TraceInformation(string.Format("Download local image for {0} {1}", this.Poster == null ? this.imagePath : this.poster.Image, imagePath));
			try
			{
				if(!string.IsNullOrEmpty(imagePath))
				{
					
					BitmapImage bmpImage = new BitmapImage();
					BitmapImage bmpImage2 = new BitmapImage();
					MemoryStream ms = new MemoryStream();
					MemoryStream ms2 = new MemoryStream();
					
					using(FileStream fileImage = File.OpenRead(imagePath))
					{
						var length = fileImage.Length;
						
						this.ImageLength = length;
						this.IsImageLengthUnknow = false;
						
						int read = 1;
						byte[] buffer = new byte[10000];
						while (read > 0)
						{
							read = fileImage.Read(buffer, 0, buffer.Length);
							
							if(read>0)
							{
								if (isThumbnail) {
									ms2.Write(buffer, 0, read);
								}
								
								ms.Write(buffer, 0, read);
								
								this.TotalBytesLoaded+=read;
							}
						}
					}
					
					if(!isThumbnail)
					{
						ms.Position = 0;
						bmpImage.BeginInit();
						bmpImage.CacheOption = BitmapCacheOption.OnLoad;
						bmpImage.StreamSource = ms;
						bmpImage.EndInit();
					}
					
					if(isThumbnail)
					{
						ms.Position = 0;
						bmpImage.BeginInit();
						bmpImage.CacheOption = BitmapCacheOption.OnLoad;
						bmpImage.StreamSource = ms;
						bmpImage.EndInit();
						
						this.ImageHeight = (int) bmpImage.Height;
						this.ImageWidth = (int) bmpImage.Width;
						this.ImageRatio = Math.Round((float)bmpImage.Width / (float)bmpImage.Height,2).ToString();
						bmpImage.StreamSource = null;
						ms.Dispose();
						bmpImage = null;
						
						var divider = this.ImageWidth>=1024 ? 8 : 3;
						ms2.Position = 0;
						bmpImage2.BeginInit();
						
						if(this.DoThumbnail)
						{
							bmpImage2.DecodePixelWidth = (int)(this.ImageWidth / divider);
							bmpImage2.DecodePixelHeight = (int)(this.ImageHeight / divider);
						}
						
						bmpImage2.CacheOption = BitmapCacheOption.OnLoad;
						bmpImage2.StreamSource = ms2;
						bmpImage2.EndInit();
					}
					
					//**THIS LINE locks the BitmapImage so that it can be transported across threads!!
					if(!isThumbnail)
					{
						bmpImage.Freeze();
					}
					else
					{
						bmpImage2.Freeze();
					}
					
					GalaSoft.MvvmLight.Threading.DispatcherHelper.CheckBeginInvokeOnUI(new Action(()=>
					                                                                              {
					                                                                              	try
					                                                                              	{
					                                                                              		if(!isThumbnail)
					                                                                              		{
					                                                                              			Tracer.TracerInstance.TraceInformation("Loaded real poster");
					                                                                              			this.hasAlreadyDownloadedRealImage = true;
					                                                                              			
					                                                                              			var previousImg = this.ZoomImageObject.Source as BitmapImage;
					                                                                              			if (previousImg!=null && previousImg.StreamSource!=null) {
					                                                                              				Tracer.TracerInstance.TraceInformation("Dispose previous image");
					                                                                              				this.ZoomImageObject.Source = null;
					                                                                              				var stream = previousImg.StreamSource;
					                                                                              				previousImg = null;
					                                                                              				stream.Dispose();
					                                                                              			}
					                                                                              			
					                                                                              			this.ZoomImageObject.Source = bmpImage;
					                                                                              			this.ZoomImageObject.Unloaded += delegate
					                                                                              			{
					                                                                              				Tracer.TracerInstance.TraceInformation("Dispose current normal image unload WPF control");
					                                                                              				previousImg = this.ZoomImageObject.Source as BitmapImage;
					                                                                              				if (previousImg!=null && previousImg.StreamSource!=null) {
					                                                                              					this.ZoomImageObject.Source = null;
					                                                                              					var stream = previousImg.StreamSource;
					                                                                              					previousImg = null;
					                                                                              					stream.Dispose();
					                                                                              				}
					                                                                              				
					                                                                              				this.hasAlreadyDownloadedRealImage = false;
					                                                                              			};
					                                                                              		}
					                                                                              		else
					                                                                              		{
					                                                                              			bmpImage = null;
					                                                                              			ms.Close();
					                                                                              			ms.Dispose();
					                                                                              		}
					                                                                              		
					                                                                              		if (isThumbnail) {
					                                                                              			var previousImg = this.ImageObject.Source as BitmapImage;
					                                                                              			if (previousImg!=null && previousImg.StreamSource!=null) {
					                                                                              				Tracer.TracerInstance.TraceInformation("Dispose previous thumbnail image");
					                                                                              				this.ImageObject.Source = null;
					                                                                              				var stream = previousImg.StreamSource;
					                                                                              				previousImg = null;
					                                                                              				stream.Dispose();
					                                                                              			}
					                                                                              			
					                                                                              			this.ImageObject.Source = bmpImage2;
					                                                                              			this.ImageObject.Unloaded += delegate
					                                                                              			{
					                                                                              				Tracer.TracerInstance.TraceInformation("Dispose current thumbnail image unload WPF control");
					                                                                              				previousImg = this.ImageObject.Source as BitmapImage;
					                                                                              				if (previousImg!=null && previousImg.StreamSource!=null) {
					                                                                              					this.ImageObject.Source = null;
					                                                                              					var stream = previousImg.StreamSource;
					                                                                              					previousImg = null;
					                                                                              					stream.Dispose();
					                                                                              				}
					                                                                              			};
					                                                                              		}
					                                                                              		
					                                                                              	}
					                                                                              	catch(Exception ex)
					                                                                              	{
					                                                                              		Tracer.TracerInstance.TraceError("Error loading image" + ex);
					                                                                              		this.IsInError = true;
					                                                                              	}
					                                                                              	finally
					                                                                              	{
					                                                                              		this.IsLoading = false;
					                                                                              	}
					                                                                              }));
					
					this.IsLoading = false;
					
				}
				else
				{
					// No image need to display something
					this.IsLoading = false;
				}
			}
			catch(Exception ex)
			{
				Tracer.TracerInstance.TraceError("Error while loading image async", ex);
				this.IsInError = true;
			}
		}
		
		private void PropertyChangedOnUI(string propertyName)
		{
			GalaSoft.MvvmLight.Threading.DispatcherHelper.CheckBeginInvokeOnUI(new Action(()=>
			                                                                              {
			                                                                              	this.RaisePropertyChanged(propertyName);
			                                                                              }));
		}
		
		private void DownloadFileAsync()
		{
			bool success = false;
			int retry = 0;
			
			while (retry<3 && !success) {
				try
				{
					HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(string.IsNullOrEmpty(this.ImagePath) ? this.Poster.Image : this.ImagePath);
					req.Timeout = 30000;
					var wp = WebRequest.DefaultWebProxy;
					wp.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
					req.Proxy = wp;
					
					long length = req.ContentLength;
					
					if (length!=-1) {
						this.IsImageLengthUnknow = false;
						this.ImageLength = length;
						this.TotalBytesLoaded = 0;
					}
					
					
					using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
						using (MemoryStream ms = new MemoryStream())
					{
						Stream r = resp.GetResponseStream();
						int read = 1;
						byte[] buffer = new byte[10000];
						while (read > 0)
						{
							read = r.Read(buffer, 0, buffer.Length);
							ms.Write(buffer, 0, read);
							
							this.TotalBytesLoaded+=read;
						}
						ms.Flush();
						ms.Seek(0, SeekOrigin.Begin);

						var newLocalPath = this.localPath;
						if (localPath.IndexOf("http://")!=-1) {
							newLocalPath = Path.Combine(Environment.GetEnvironmentVariable("TEMP"),Guid.NewGuid().ToString() + "." + Path.GetExtension(this.localPath));
							this.localPath = newLocalPath;
						}
						
						if (string.IsNullOrEmpty(this.localPath)) {
							throw new InvalidOperationException("No image (Poster control)");
						}
						
						using (var stream = ProtectedFileStream.OpenExclusiveWriter(this.localPath))
						{
							stream.Write(ms.ToArray(), 0, (int)ms.Length);
						}
						ms.Close();
					}
					
					// Create the id
					if(this.Poster!=null)
					{
						this.Poster.Id = mveEngine.Helper.GetMD5(this.Poster.Image);
						
						BitmapMetadataHelper.WriteMetadata(this.localPath, TypeBitmapMetadata.UniqueId, this.Poster.Id);
					}
					
					this.ImagePath = localPath;
					
					success = true;
				}
				catch(Exception ex)
				{
					MetaVideoEditorEvolved.Helper.Tracer.TracerInstance.TraceError(string.Format("Error downloading image {0}",this.Poster == null ? this.ImagePath : this.Poster.Image), ex);
				}
				finally
				{
					retry++;
				}
			}
			
			if (success) {
				this.DownloadLocalImageAsync(localPath,true);
			}
			else
			{
				this.IsInError = true;
			}
		}
		
		private MemoryStream CreateThumbnail(System.Drawing.Image original, int newWidth, int newHeight)
		{
			MemoryStream responseStream = new MemoryStream();
			System.Drawing.Image image = original;
			System.Drawing.Image thumbnail = new System.Drawing.Bitmap( newWidth, newHeight );
			System.Drawing.Graphics graphic = System.Drawing.Graphics.FromImage( thumbnail );
			
			graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
			graphic.SmoothingMode = SmoothingMode.HighQuality;
			graphic.PixelOffsetMode = PixelOffsetMode.HighQuality;
			graphic.CompositingQuality = CompositingQuality.HighQuality;
			
			graphic.DrawImage(image, 0, 0, newWidth, newHeight);
			
			using ( thumbnail )
			{
				thumbnail.Save(responseStream, ImageFormat.Jpeg);
			}

			return responseStream;
		}
	}
}
